import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { SysMenu } from './entities/menu.entity';
import { StringUtils } from '../../common/utils/StringUtils';
import { StatusEnum } from '../../common/enums/StatusEnum';
import { MenuTypeEnum } from '../../common/enums/MenuTypeEnum';
import { SystemConstant } from '../../common/constant/SystemConstant';
import { Result } from '../../common/result/Result';
import { Repository } from 'typeorm';
import { MenuForm } from './dto/menu.form';
import { JwtUtils } from '../01_auth/jwt.utils';
import { generator } from '../../common/utils/generator';
import { JwtPayload } from '../01_auth/jwt.payload';

@Injectable()
export class MenuService {
  constructor(
    @InjectRepository(SysMenu)
    private menuRepository: Repository<SysMenu>,
    private jwt: JwtUtils,
  ) {}

  async getRoutes() {
    const routes = await this.listRoutes();
    return Result.success(
      generator.buildTree(routes, SystemConstant.ROOT_NODE_ID),
    );
  }

  async listRoutes() {
    const type = MenuTypeEnum.BUTTON.label;
    // 1. 查询出所有的菜单
    const menus = await this.menuRepository
      .createQueryBuilder('u')
      .where('u.type != :type', { type: type })
      .getMany();
    // 2. 根据菜单查询出权限CODE编码
    const routes: any[] = [];
    for (const menu of menus) {
      const route = {
        id: menu.id,
        parentId: menu.parentId,
        name: StringUtils.capitalize(StringUtils.toCamelCase(menu.name)), // 根据name路由跳转 this.$router.push({name:xxx})
        path: menu.path, // 根据path路由跳转 this.$router.push({path:xxx})
        component: menu.component,
        redirect: menu.redirect,
        meta: {
          title: menu.name,
          icon: menu.icon,
          roles: await this.getRoleCodeByMenuId(menu.id),
          hidden: StatusEnum.DISABLE.value == menu.visible,
          keepAlive: false,
          alwaysShow: false,
        },
        children: [],
      };
      // 【菜单】是否开启页面缓存
      if (MenuTypeEnum.MENU.label == menu.type && menu.keepAlive == 1) {
        route.meta.keepAlive = true;
      }
      // 【目录】只有一个子路由是否始终显示
      if (MenuTypeEnum.CATALOG.label == menu.type && menu.alwaysShow == 1) {
        route.meta.alwaysShow = true;
      }
      routes.push(route);
    }
    return routes;
  }

  async getRoleCodeByMenuId(menuId: number) {
    const codes = await this.menuRepository.query(
      `SELECT code FROM sys_role t1 LEFT JOIN sys_role_menu t2 ON t1.id = t2.role_id WHERE t2.menu_id = '${menuId}'`,
    );
    const result: any[] = [];
    codes.forEach((code) => {
      result.push(code.code);
    });
    return result;
  }

  async listMenus(keywords: string) {
    // 1. 查询出所有的菜单
    const queryBuilder = await this.menuRepository
      .createQueryBuilder('su')
      .orderBy('sort');
    if (keywords) {
      queryBuilder.andWhere(
        '(su.name LIKE :keywords OR su.type LIKE :keywords)',
        { keywords: `%${keywords}%` },
      );
    }
    const menus = await queryBuilder.getMany();
    // 2. 获取所有的菜单ID
    const menuIds: Set<number> = new Set();
    menus.forEach((menu) => {
      menuIds.add(menu.id);
    });
    // 3. 获取所有父级ID
    const parentIds: Set<number> = new Set();
    menus.forEach((menu) => {
      parentIds.add(menu.parentId);
    });
    const parentIdsArray = Array.from(parentIds);
    // 3. 获取根节点ID（递归的起点），即父节点ID中不包含在部门ID中的节点，
    // 注意这里不能拿顶级菜单 O 作为根节点，因为菜单筛选的时候 O 会被过滤掉
    const rootIds: number[] = [];
    for (const id of parentIdsArray) {
      if (!menuIds.has(id)) {
        rootIds.push(id);
      }
    }
    // 4. 格式化数据
    const menuVOs = [];
    for (const menu of menus) {
      const menuVO = {
        id: menu.id,
        parentId: menu.parentId,
        name: menu.name,
        type: menu.type,
        path: menu.path,
        component: menu.component,
        sort: menu.sort,
        visible: menu.visible,
        icon: menu.icon,
        redirect: menu.redirect,
        perm: menu.perm,
        children: [],
      };
      menuVOs.push(menuVO);
    }
    // 4. 使用递归函数来构建菜单树
    // console.log(rootIds, menuVOs.length);
    const result = generator.buildTree(menuVOs, 0);
    return Result.success(result);
  }

  async deleteMenu(id: number) {
    await this.menuRepository.softDelete({ id: id });
    return Result.success();
  }

  async addMenu(form: MenuForm, token: string) {
    console.log(112333444);
    if (form.type == MenuTypeEnum.CATALOG.label) {
      // 如果是外联
      const path = form.path;
      if (form.parentId == 0 && !form.path.startsWith('/')) {
        form.path = '/' + path;
        form.component = 'Layout';
      } else if (form.type == MenuTypeEnum.EXTLINK.label) {
        // 如果是目录
        form.component = null;
      }
    }
    const currentUser = this.jwt.decodeToken(token);
    const menu = new SysMenu({
      createUser: currentUser.nickname,
      createBy: currentUser.userId,
      updateUser: currentUser.nickname,
      updateBy: currentUser.userId,
    });
    menu.type = form.type;
    menu.perm = form.perm;
    menu.parentId = form.parentId;
    menu.component = form.component;
    menu.icon = form.icon;
    menu.path = form.path;
    menu.sort = form.sort;
    menu.name = form.name;
    menu.visible = form.visible;
    menu.treePath = await this.generatorTreePath(form.parentId);
    await this.menuRepository.save(menu);
    return Result.success();
  }
  async generatorTreePath(parentId: number) {
    if (SystemConstant.ROOT_NODE_ID == parentId) {
      return String(parentId);
    } else {
      const parent = await this.menuRepository.findOneBy({ id: parentId });
      return parent != null ? parent.treePath + ',' + parent.id : null;
    }
  }

  async menuOptions() {
    const menus = await this.menuRepository
      .createQueryBuilder()
      .orderBy('sort')
      .getMany();
    // 重构
    const menuVOs = [];
    for (const menu of menus) {
      const vo = {
        id: menu.id,
        parentId: menu.parentId,
        label: menu.name,
        value: menu.id,
      };
      menuVOs.push(vo);
    }
    const result = generator.buildTree(menuVOs, SystemConstant.ROOT_NODE_ID);
    return Result.success(result);
  }

  async getMenuForm(id: number) {
    const v = await this.menuRepository.findOneBy({ id: id });
    if (v == null) {
      return null;
    }
    const result = {
      id: v.id,
      parentId: v.parentId,
      name: v.name,
      type: v.type,
      path: v.path,
      component: v.component,
      perm: v.perm,
      visible: v.visible,
      sort: v.sort,
      icon: v.icon,
      redirect: v.redirect,
      keepAlive: v.keepAlive,
      alwaysShow: v.alwaysShow,
    };
    return Result.success(result);
  }

  async updateMenu(id: number, form: MenuForm, current: JwtPayload) {
    if (form.type == MenuTypeEnum.CATALOG.label) {
      // 如果是外联
      const path = form.path;
      if (form.parentId == 0 && !form.path.startsWith('/')) {
        form.path = '/' + path;
        form.component = 'Layout';
      } else if (form.type == MenuTypeEnum.EXTLINK.label) {
        // 如果是目录
        form.component = null;
      }
    }
    const menu = new SysMenu({
      updateUser: current.nickname,
      updateBy: current.userId,
    });
    menu.type = form.type;
    menu.perm = form.perm;
    menu.name = form.name;
    menu.parentId = form.parentId;
    menu.component = form.component;
    menu.icon = form.icon;
    menu.path = form.path;
    menu.sort = form.sort;
    menu.redirect = form.redirect;
    menu.treePath = await this.generatorTreePath(form.parentId);
    await this.menuRepository.update({ id: id }, menu);
    return Result.success();
  }

  async updateMenuVisible(id: number, visible: number) {
    await this.menuRepository.update({ id: id }, { visible: visible });
    return Result.success();
  }
}
